import greenfoot.*;
import java.util.List;
import java.util.ArrayList;
import java.util.HashSet;
import java.lang.Math;

/**
 * Mapa en el que se desarrolla el combate.
 * 
 * @author Mario Alberto Valencia Valero
 * 
 */
public class Mapa extends World
{
    protected int nivel = 1; /** Nivel que se está jugando */

    /** Precios de las unidades */
    protected int precioSoldado = 1000,
        precioArmaAntitanque = 4000,
        precioJeep = 4000,
        precioBMP = 5000,
        precioMech1 = 7000,
        precioTanque = 10000,             
        precioMech2 = 20000;
    
    /** Dinero que tienen el jugador y la computadora */
    protected int dineroJugador = 30000,
        dineroComp = 30000;   
    
    protected int turnosDinero = 100; /** Cada cuantos turnos se incrementa el dinero */
    protected int turnosCompra = 100; /** Cada cuantos turnos compra unidades la computadora */
        
    /** Contadores que muestran el dinero del jugador y de la computadora */
    private Contador contDineroJugador,
             contDineroComp;
        
    protected Fabrica fabricaSel; /** Fábrica seleccionada para comprar unidades */
             
    protected HashSet<Unidad> unidadesSel; /** Unidades seleccionadas para darles órdenes */
    
    protected MouseInfo m;
    
    protected int c1x, c1y, c2x, c2y; /** Coordenadas del rectángulo de selección */ 
    
    /**
     * Constructor
     * @param x  tamaño en x del mapa
     * @param y  tamaño en y del mapa
     */
    public Mapa(int x, int y)
    {
        super(x,y,1);
        unidadesSel = new HashSet<Unidad>();
        setPaintOrder(Contador.class, ImpactoBala.class, Disparo.class, Unidad.class);
        contDineroComp = new Contador("$");
        contDineroJugador = new Contador("$");
        addObject( contDineroComp, 50, 20 );
        addObject( contDineroJugador, 650, 20 );
    }
    
    /**
     * Verificar si se ganó, o si el jugador presionó el mouse o una tecla.
     */
    public void act()
    {
        if(checaGanar(Unidad.Color.ROJO))
            ganaste();
        if(checaGanar(Unidad.Color.VERDE))
            perdiste();
        
        m = Greenfoot.getMouseInfo();
        
        if(m!=null)
        {
            checaMovimiento();
            checaIniSeleccion();
            checaFinSeleccion();
            checaSeleccion();
            checaOrdenaCaptura();
            checaSeleccionFabrica();
        }
        
        actualizaMarcasSeleccion();
        capturaEdificios();
        
        turnosDinero--;
        if(turnosDinero<=0)
        {
            turnosDinero = 100;
            actualizaFondosJugador();
            actualizaFondosComp();
        }
        
        jugadorCompraUnidades();
        
        turnosCompra--;
        if(turnosCompra <= 0)
        {
            turnosCompra = 100;
            compCompraUnidades();
        }
        
        actualizaContadores();
    }
    
    /** 
     * Checa si se hizo click sobre el terreno.
     * Si es así, se le ordena a las unidades seleccionadas moverse.
     */
    private void checaMovimiento()
    {
        if( m != null  &&  m.getButton() != 0  &&  m.getActor() == null && Greenfoot.mouseClicked(this)
            && !Greenfoot.mouseDragged(null) && !Greenfoot.mouseDragEnded(null) )
        {
            for( Unidad uSel: unidadesSel )
                if( uSel != null )
                {
                    uSel.objx = m.getX();
                    uSel.objy = m.getY();
                    uSel.acc = Unidad.Accion.MOVER;
                }
        }
    }
    
    /**
     * Checa si se está iniciando la selección de unidades haciendo drag con el mouse.
     * Si es así, se ponen las coordenadas en c1x, c1y.
     */
    private void checaIniSeleccion()
    {
        if( Greenfoot.mousePressed(null) )
        {
            c1x = m.getX(); c1y = m.getY();
        }
    }

    /**
     * Checa si se está acabando de hacer una selección rectangular con el mouse.
     * Si es así, se seleccionan dichas unidades.
     */
    private void checaFinSeleccion()
    {
        if( Greenfoot.mouseDragEnded(null) )//this) )
        {
            c2x = m.getX(); c2y = m.getY();
            
            int p1x = Math.min(c1x, c2x), p1y = Math.min(c1y, c2y),
                p2x = Math.max(c1x, c2x), p2y = Math.max(c1y, c2y);
            
            unidadesSel.clear();
                
            for( Unidad u: (List<Unidad>)getObjects(Unidad.class) )
            {
                if( p1x <= u.getX() && u.getX() <= p2x && p1y <= u.getY() && u.getY() <= p2y )
                    unidadesSel.add(u);
            }
        }
    }
    
    /**
     * Checa si se hizo click directo sobre una unidad para seleccionarla.
     * Si es así, se selecciona dicha unidad.
     */
    private void checaSeleccion()
    {
        for(Unidad u: (List<Unidad>)getObjects(Unidad.class))
        {               
            if(Greenfoot.mouseClicked(u))
            {
                if(u.jugador)
                {
                    unidadesSel.clear();
                    unidadesSel.add(u);
                }
                else{
                    for( Unidad uSel: unidadesSel )
                        if( uSel != null )
                        {
                            uSel.nuevoObjetivo(u);
                            uSel.acc = Unidad.Accion.ATACAR;
                        }
                }
            }    
        }
    }
    
    /**
     * Checa si se hizo click directo sobre un edificio para capturarlo.
     * Si es así, se le ordena a las unidades seleccionadas capturarlo.
     */
    private void checaOrdenaCaptura()
    {
        for(Edificio e: (List<Edificio>)getObjects(Edificio.class))
        {
            if(Greenfoot.mouseClicked(e) && e.col!=Unidad.Color.ROJO)
            {
                for(Unidad uSel: unidadesSel)
                {
                    if(uSel!=null && uSel.getClass()==Soldado.class)
                        ((Soldado)uSel).nuevaCaptura(e);
                }
            }
        }
    }
    
    /**
     * Checa si se hizo click sobre una fábrica capturada.
     * Si es así, se selecciona dicha fábrica.
     */
    private void checaSeleccionFabrica()
    {
        for(Fabrica f: (List<Fabrica>)getObjects(Fabrica.class))
        {
            if(Greenfoot.mouseClicked(f) && f.col==Unidad.Color.ROJO)
            {
                if(fabricaSel != null) fabricaSel.desSeleccionar();
                fabricaSel = f;
                f.seleccionar();
            }
        }
    }
    
    /**
     * Actualiza la posición de las marcas que muestran cuales unidades están seleccionadas.
     */
    private void actualizaMarcasSeleccion()
    {
        for( Seleccion s: (List<Seleccion>)getObjects(Seleccion.class) )
            removeObject(s);
        for(Unidad uSel: unidadesSel){
            if(uSel != null && uSel.getWorld() != null)
                addObject(new Seleccion(), uSel.getX(), uSel.getY()-15);                    
        }
    }   
    
    /**
     * Checa cuales soldados están sobre edificios, y captura los edificios.
     */
    private void capturaEdificios()
    {
        for( Soldado s: (List<Soldado>)getObjects(Soldado.class) )
        {
            Edificio capt = (Edificio)s.getOneIntersectingObjectPlease(Edificio.class);
            if(capt!=null  &&  capt.col != s.color)
                capt.captura(s.color);
        }
    }
    
    /**
     * Checa si el jugador presionó una tecla para comprar una unidad.
     * Si es así, se compra dicha unidad.
     */
    private void jugadorCompraUnidades()
    {
        String key = Greenfoot.getKey();
        if(key == null  ||  fabricaSel == null || fabricaSel.getWorld()==null) return;

        if(key.equals("1"))
        {
            if(dineroJugador >= precioSoldado)
            {
                dineroJugador -= precioSoldado;
                addObject( new Soldado(Unidad.Color.ROJO, true), fabricaSel.getX(), fabricaSel.getY() );
            }
        }

        if(key.equals("2"))
        {
            if(dineroJugador >= precioArmaAntitanque)
            {
                dineroJugador -= precioArmaAntitanque;
                addObject( new ArmaAntitanque(Unidad.Color.ROJO, true), fabricaSel.getX(), fabricaSel.getY() );
            }
        }
 
        if(key.equals("3"))
        {
            if(dineroJugador >= precioJeep)
            {
                dineroJugador -= precioJeep;
                addObject( new Jeep(Unidad.Color.ROJO, true), fabricaSel.getX(), fabricaSel.getY() );
            }
        }
 
        if(key.equals("4"))
        {
            if(dineroJugador >= precioBMP)
            {
                dineroJugador -= precioBMP;
                addObject( new BMP(Unidad.Color.ROJO, true), fabricaSel.getX(), fabricaSel.getY() );
            }
        }
        
        if(key.equals("5") && nivel >= 2)
        {
            if(dineroJugador >= precioMech1)
            {
                dineroJugador -= precioMech1;
                addObject( new Mech1(Unidad.Color.ROJO, true), fabricaSel.getX(), fabricaSel.getY() );
            }
        }
        
        if(key.equals("6") && nivel >= 2)
        {
            if(dineroJugador >= precioTanque)
            {
                dineroJugador -= precioTanque;
                addObject( new Tanque_ligero(Unidad.Color.ROJO, true), fabricaSel.getX(), fabricaSel.getY() );
            }
        }      
        
        if(key.equals("7") && nivel >= 3)
        {
            if(dineroJugador >= precioMech2)
            {
                dineroJugador -= precioMech2;
                addObject( new Mech2(Unidad.Color.ROJO, true), fabricaSel.getX(), fabricaSel.getY() );
            }
        }      
    }
    
    /**
     * La computadora compra unidades al azar.
     */
    private void compCompraUnidades()
    {
        for(Edificio e: (List<Edificio>)getObjects(Fabrica.class))
        {
            if(e.col==Unidad.Color.VERDE)
            {
                int maxU = 4; // maxima unidad que se puede comprar
                if(nivel == 2)
                    maxU = 6;
                if(nivel == 3)
                    maxU = 7;
                int r = Greenfoot.getRandomNumber(maxU)+1;
                if(r==1)
                {
                    if(dineroComp >= precioSoldado)
                    {
                        dineroComp -= precioSoldado;
                        addObject( new Soldado(Unidad.Color.VERDE, false), e.getX(), e.getY() );
                    }
                }
                
                if(r==2)
                {
                    if(dineroComp >= precioArmaAntitanque)
                    {
                        dineroComp -= precioArmaAntitanque;
                        addObject( new ArmaAntitanque(Unidad.Color.VERDE, false), e.getX(), e.getY() );
                    }
                }
                
                if(r==3)
                {
                    if(dineroComp >= precioJeep)
                    {
                        dineroComp -= precioJeep;
                        addObject( new Jeep(Unidad.Color.VERDE, false), e.getX(), e.getY() );
                    }
                }
                
                if(r==4)
                {
                    if(dineroComp >= precioBMP)
                    {
                        dineroComp -= precioBMP;
                        addObject( new BMP(Unidad.Color.VERDE, false), e.getX(), e.getY() );
                    }
                }
         
                if(r==5)
                {
                    if(dineroComp >= precioMech1)
                    {
                        dineroComp -= precioMech1;
                        addObject( new Mech1(Unidad.Color.VERDE, false), e.getX(), e.getY() );
                    }
                }
                
                if(r==6)
                {
                    if(dineroComp >= precioTanque)
                    {
                        dineroComp -= precioTanque;
                        addObject( new Tanque_ligero(Unidad.Color.VERDE, false), e.getX(), e.getY() );
                    }
                }
                
                if(r==7)
                {
                    if(dineroComp >= precioMech2)
                    {
                        dineroComp -= precioMech2;
                        addObject( new Mech2(Unidad.Color.VERDE, false), e.getX(), e.getY() );
                    }
                }       
            }
        }
    }
    
    /**
     * Se le agregan $1000 al jugador por cada edificio capturado.
     */
    private void actualizaFondosJugador()
    {
        for(Edificio e: (List<Edificio>)getObjects(Edificio.class))
        {
            if(e.col==Unidad.Color.ROJO)
            {
                dineroJugador += 1000;
            }
        }
    }
    
    /**
     * Se le agregan $1000 a la computadora por cada edificio capturado.
     */
    private void actualizaFondosComp()
    {
        for(Edificio e: (List<Edificio>)getObjects(Edificio.class))
        {
            if(e.col==Unidad.Color.VERDE)
            {
                dineroComp += 1000;
            }
        }
    }
    
    /**
     * Se actualizan los contadores de dinero.
     */
    private void actualizaContadores()
    {
        contDineroJugador.ponValor(dineroJugador);
        contDineroComp.ponValor(dineroComp);
    }
    
    /**
     * Checa si el jugador dado ganó.
     * @param col  el color del jugador
     */
    private boolean checaGanar(Unidad.Color col)
    {
        /* Se gana si se capturó la HQ */
        boolean capturada = true;
        for(Edificio e: (List<Edificio>)getObjects(HQ.class))
        {
            if(e.col!=col && e.col!=Unidad.Color.GRIS)
            {
                capturada = false;
                break;
            }
        }
        if(capturada)
            return true;
            
        /* Se gana si se destruyeron todas las unidades enemigas, y se capturaron todas sus fábricas */
        boolean sinEnemigos = true;
        for(Unidad u: (List<Unidad>)getObjects(Unidad.class))
        {
            if(u.color!=col)
            {
                sinEnemigos = false;
                break;
            }
        }
        if(!sinEnemigos)
            return false;
        
        boolean sinFabricas = true;
        for(Fabrica f: (List<Fabrica>)getObjects(Fabrica.class))
        {
            if(f.col!=col && f.col!=Unidad.Color.GRIS)
            {
                sinFabricas = false;
                break;
            }
        }
        
        if(sinFabricas)
            return true;
        return false;
    }
    
    /**
     * Muestra el mensaje de que ganaste la batalla, y pasa al siguiente mapa.
     */
    private void ganaste()
    {
        addObject( new Mensaje("GANASTE", 40), 300, 200 );
        Greenfoot.delay(5);
        if(nivel==1)
            Greenfoot.setWorld(new Mapa2());
        if(nivel==2)
            Greenfoot.setWorld(new Mapa3());
        if(nivel==3)
            Greenfoot.setWorld(new Portada());
    }
    
    /**
     * Muestra el mensaje de que perdiste la batalla, y regresa a la portada.
     */
    private void perdiste()
    {
        addObject( new Mensaje("PERDISTE", 40), 300, 200 );
        Greenfoot.delay(50);
        Greenfoot.setWorld(new Portada());
    }
}
